#ifndef _Ring_QUEUE_HPP_
#define _Ring_QUEUE_HPP_

#include <iostream>
#include <vector>
#include <pthread.h>
#include "sem.hpp"

const int g_DefaultNum = 5;

template <class T>
class RingQueue
{
public:
    RingQueue(int defaultNum = g_DefaultNum, int num = g_DefaultNum,
              int pstep = 0, int cstep = 0,
              int spaceSem = g_DefaultNum, int dataSem = 0)
        : _rqueue(defaultNum),
          _num(num),
          _pstep(pstep),
          _cstep(cstep),
          _spaceSem(spaceSem),
          _dataSem(dataSem)
    {
        pthread_mutex_init(&cLock, nullptr);
        pthread_mutex_init(&pLock, nullptr);
    }

    ~RingQueue()
    {
        pthread_mutex_destroy(&cLock);
        pthread_mutex_destroy(&pLock);
    }

    // 生产者需要空间资源 生产者们的临界资源是 下标
    void push(const T &in)
    {
        /*index只在该函数内可见 外部无法获取 不使用
        static int index = 0;
        _rqueue[index] = in;
        */
        _spaceSem.p();              // 阻塞等待减少生产者空间资源Sem值 生产者发送资源的前提是队列中有空间
        pthread_mutex_lock(&pLock); // 保证多线程生产者环境下 只有一个生产者线程进入
        // 生产者发送资源到队列
        _rqueue[_pstep++] = in;
        _pstep %= _num;
        pthread_mutex_unlock(&pLock);
        // 现已确定队列中肯定有资源
        _dataSem.v(); // 增加消费者数据资源Sem值 使得消费者等待信号量时知道队列中有数据可取
    }
    
    // 消费者需要数据资源 消费者们的临界资源是下标
    void pop(T *out)
    {
        _dataSem.p();               // 阻塞等待减少消费者数据资源Sem值 消费者获取资源的前提是队列中有资源可取
        pthread_mutex_lock(&cLock); // 保证多线程消费者环境下 只有一个消费者线程进入
        // 消费者从队列中获取资源
        *out = _rqueue[_cstep++];
        _cstep %= _num;
        pthread_mutex_unlock(&cLock);
        // 现已确定队列中肯定有空间
        _spaceSem.v(); // 增加生产者空间资源Sem值 使得生产者等待信号量时知道队列中有空间可放
    }

private:
    std::vector<T> _rqueue;
    int _num;
    int _pstep; // 生产者当前的位置
    int _cstep; // 消费者当前的位置
    Sem _spaceSem;
    Sem _dataSem;
    pthread_mutex_t cLock;
    pthread_mutex_t pLock;
};

#endif